package com.xianggu.mybatis.proxyFactory;

import com.xianggu.mybatis.annotation.*;
import com.xianggu.mybatis.enums.SqlExecuteTypeEnum;
import com.xianggu.mybatis.headlerImpl.TypeHandlerUtils;
import com.xianggu.mybatis.mapper.MyBatisSqlParser;
import com.xianggu.mybatis.mapper.entry.MybatisSqlVo;
import com.xianggu.mybatis.paging.Page;
import com.xianggu.mybatis.utils.SqlParserTool;
import com.xianggu.mybatis.utils.TypeHandler;
import com.xianggu.spring.transaction.context.TransactionContext;

import java.lang.reflect.*;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author chengjy
 * @date 2025/3/19
 */
public class MybatisProxyFactory implements InvocationHandler {

    private static String sysPagePath = "java.lang";

    private static String countSQl = "select count(1) as num from (%s) as a";
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        Class<?> declaringClass = method.getDeclaringClass();

        // 忽略普通方法、如 toString、hashCode、equals等方法
        if (declaringClass.equals(Object.class)) {
            System.out.println("忽略普通方法" + methodName + "类型 " + declaringClass);
            return method.invoke(proxy, args);
        }
        
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        
        Parameter[] parameters = method.getParameters();
        Map<String, Object> paramMap = new HashMap<>(parameters.length);
        String pageSql = "";
        Page page = null;
        // 解析方法的参数，主要是分页参数
        Result result = getResult(args, parameters, paramMap, page, pageSql);

        // 解析注解中的sql
        handlerSqlType sqlType = getHandlerSqlType(method, declaringClass, paramMap, methodName);


        // 将sql中的占位符替换为 ? 号，并且按顺序指定 ？ 号对应的属性名
        List<String> sortedParamNames = new ArrayList<>();
        String parseSql = SqlParserTool.parseSql(sqlType.sql(), sortedParamNames);
        try {
            conn = TransactionContext.getConnection();

            // 预编译SQL
            System.out.println("执行sql：" + parseSql + result.pageSql());
            pstmt = conn.prepareStatement(parseSql + result.pageSql());

            boolean isBaseTypeFlag = paramMap.size() == 1;

            // 设置参数
            setSqlParam(sortedParamNames, isBaseTypeFlag, paramMap, pstmt);

            // 执行查询
            if (sqlType.executeType() == SqlExecuteTypeEnum.SELECT) {
                rs = pstmt.executeQuery();
            } else {
                // 判断返回值类型是否为 字符串或数值
                Class<?> returnType = method.getReturnType();
                if (returnType == String.class || returnType == Integer.class || returnType == int.class) {
                    // 直接返回影响行数
                    TypeHandler typeHandler = TypeHandlerUtils.getTypeHandler(returnType);
                    int i = pstmt.executeUpdate();
                    return typeHandler.toBaseType(i);
                } else {
                    throw new RuntimeException("返回值类型不正确、增删改的返回类型为影响行数，请指定正确的返回值类型接受");
                }
            }
            rs = pstmt.executeQuery();

            // 拿到返回结果泛型
            Class resultType = null;
            Type genericReturnType = method.getGenericReturnType();
            if (genericReturnType instanceof Class) {
                // 说明不是泛型
                resultType = (Class) genericReturnType;
            } else if (genericReturnType instanceof ParameterizedType) {
                ParameterizedType parameterizedType = (ParameterizedType) genericReturnType;
                // 获取实际类型参数（这里是List中的泛型类型，即Map<String, Object>对应的Type）
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                if (actualTypeArguments.length > 0) {
                    Type mapType = actualTypeArguments[0];
                    if (mapType instanceof ParameterizedType) {
                        ParameterizedType mapParameterizedType = (ParameterizedType) mapType;
                        resultType = (Class) mapParameterizedType.getRawType();
                    }else{
                        resultType = (Class) mapType;
                    }
                }
            }



            // 根据返回结果包含的字段属性
            List<String> columnNames = new ArrayList<>();

            ResultSetMetaData metaData = rs.getMetaData();
            for (int i = 0; i < metaData.getColumnCount(); i++) {
                String columnLabel = metaData.getColumnLabel(i + 1);
                // 获取所有属性名
                columnNames.add(columnLabel);
            }

            List<Object> resultList = new ArrayList<>();
            while (rs.next()) {
                // Map类型不能用无参构造new对象 需要单独处理
                if (Map.class.equals( resultType)) {
                    Map<String, Object> map = new HashMap<>();
                    for (String columnName : columnNames) {
                        // 获取属性
                        map.put(columnName, rs.getObject(columnName));
                    }
                    resultList.add(map);
                    continue;
                }
                Object o = null;
                if (resultType != null) {
                    o = resultType.newInstance();
                }
                for (String columnName : columnNames) {
                    TypeHandler typeHandler = TypeHandlerUtils.getTypeHandler(resultType);
                    // 泛型如果是基本数据类型，直接返回
                    if (sysPagePath.equals(resultType.getPackageName())) {
                        o = typeHandler.getParameterValue(rs, columnName);
                        continue;
                    }else{
                        // 字段转驼峰
                        String toCamelCase = columnName;
                        if (columnName.contains("_")) {
                            toCamelCase = SqlParserTool.convertToCamelCase(columnName);
                        }
                        try {
                            Field field = resultType.getDeclaredField(toCamelCase);
                            field.setAccessible(true);
                            Class<?> fieldType = field.getType();
                            typeHandler = TypeHandlerUtils.getTypeHandler(fieldType);
                            Object resultValue = typeHandler.getParameterValue(rs, columnName);
                            field.set(o, resultValue);
                        } catch (NoSuchFieldException e) {
//                                throw new RuntimeException(resultType + "实体不存在["+ toCamelCase +"]字段不存在!" + e);
                            //此处不能抛异常，允许少字段的情况存在
//                                System.out.println(resultType + "实体不存在["+ toCamelCase +"]字段不存在!" + e);
                        }
                    }
                }
                resultList.add(o);
            }
            // 查询为空
            if (resultList.isEmpty()) {
                return null;
            }

            // 判断返回值类型
            Class<?> returnType = method.getReturnType();
            if (returnType.equals(List.class)) {
                return resultList;
            } else if (returnType.equals(Page.class) && result.page() != null) {
                // 分页查询
                String totalSql = String.format(countSQl, parseSql);
                pstmt = conn.prepareStatement(totalSql);
                setSqlParam(sortedParamNames, isBaseTypeFlag, paramMap, pstmt);
                // 获取总条数
                ResultSet resultSet = pstmt.executeQuery();
                if (resultSet.next()) {
                    Object total = resultSet.getObject("num");
                    return result.page().setRecords(resultList, Integer.valueOf(total.toString()));
                }
                return null;
            } else if (returnType.equals(Map.class)) {
                //TODO Mybatis中 map类型的返回值怎么写
                return null;
            } else {
                return resultList.get(0);
            }
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException("SQL执行异常：" + e);
        } finally {
            // 关闭资源
            try {
                if (rs != null) rs.close();
                if (pstmt != null) pstmt.close();
//                        if (conn != null) conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    private static handlerSqlType getHandlerSqlType(Method method, Class<?> declaringClass, Map<String, Object> paramMap, String methodName) {
        String sql = "";
        SqlExecuteTypeEnum executeType = null;
        if (method.isAnnotationPresent(Select.class)) {
            executeType = SqlExecuteTypeEnum.SELECT;
            Select select = method.getAnnotation(Select.class);
            sql = select.value();
        } else if (method.isAnnotationPresent(Insert.class)) {
            executeType = SqlExecuteTypeEnum.INSERT;
            Insert insert = method.getAnnotation(Insert.class);
            sql = insert.value();
        } else if (method.isAnnotationPresent(Update.class)) {
            executeType = SqlExecuteTypeEnum.UPDATE;
            Update insert = method.getAnnotation(Update.class);
            sql = insert.value();
        } else if (method.isAnnotationPresent(Delete.class)) {
            executeType = SqlExecuteTypeEnum.DELETE;
            Delete insert = method.getAnnotation(Delete.class);
            sql = insert.value();
        } else {
            // xml文件中解析sql
            String xmlPath = declaringClass.getPackageName().replace(".", "/") + "/" + declaringClass.getSimpleName() + ".xml";
            String resourcePath = declaringClass.getClassLoader().getResource(xmlPath).getPath();
            Map<String, MybatisSqlVo> map = MyBatisSqlParser.parseMapperXml(resourcePath , paramMap);
            if (map.containsKey(methodName)) {
                MybatisSqlVo mybatisSqlVo = map.get(methodName);
                sql = mybatisSqlVo.getSql();
                // 获取执行类型
                executeType = mybatisSqlVo.getType();
            }
        }
        handlerSqlType h = new handlerSqlType(sql, executeType);
        return h;
    }

    private record handlerSqlType(String sql, SqlExecuteTypeEnum executeType) {
    }

    private static Result getResult(Object[] args, Parameter[] parameters, Map<String, Object> paramMap, Page page, String pageSql) throws IllegalAccessException {
        if (parameters.length == 1 && !sysPagePath.equals(args[0].getClass().getPackageName())) {
            // 如果方法只有一个参数，并且参数类型是Class，则直接将参数属性取出来作为参数
            Object arg = args[0];
            Class aClass = args[0].getClass();
            if (Page.class.equals(aClass)) {
                // 分页参数
                page = (Page) arg;
                pageSql = String.format(" limit %s , %s ", page.getOffset(), page.getPageSize());
            }else{
                // 反射获取所有可用参数
                Field[] declaredFields = aClass.getDeclaredFields();
                for (Field declaredField : declaredFields) {
                    declaredField.setAccessible(true);
                    Object o = declaredField.get(arg);
                    paramMap.put(declaredField.getName(), o);
                }
            }
        } else {
            for (int i = 0; i < parameters.length; i++) {
                // 方法参数
                Parameter parameter = parameters[i];
                String paramName = parameter.getName();
                if (parameter.isAnnotationPresent(Param.class)) {
                    Param parameterAnnotation = parameter.getAnnotation(Param.class);
                    paramName = parameterAnnotation.value();
                }
                if (Page.class.equals(parameter.getType())) {
                    // 分页参数
                    page = (Page) args[i];
                    pageSql = String.format(" limit %s , %s ", page.getOffset(), page.getPageSize());
                } else {
                    // 将参数值根据 方法参数顺序取出存入映射map
                    paramMap.put(paramName, args[i]);
                }
            }
        }
        Result result = new Result(pageSql, page);
        return result;
    }

    private record Result(String pageSql, Page page) {
    }

    private static void setSqlParam(List<String> sortedParamNames, boolean isBaseTypeFlag, Map<String, Object> paramMap, PreparedStatement pstmt) {
        for (int i = 0; i < sortedParamNames.size(); i++) {
            int index = i + 1;
            String pName = sortedParamNames.get(i);
            // 说明只有单个传参，无需更具参数名获取
            Object o = null;
            if (isBaseTypeFlag) {
                o = paramMap.values().stream().findFirst().get();
            } else {
                // 根据参数名获取参数值
                o = paramMap.get(pName);
            }

            // 根据Object类型获取对应的类型处理器
            try {
                if (o == null){
                    pstmt.setObject(index, "");
                }else{
                    TypeHandler typeHandler = TypeHandlerUtils.getTypeHandler(o.getClass());
                    typeHandler.setPreparedStatementValue(pstmt, index, o , pName);
                }
                } catch (NullPointerException e) {
                throw new RuntimeException("参数类型不匹配，请检查SQL参数类型是否正确：" + sortedParamNames);
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
